unit DIrr.Q3Shader;

{$I DIrr.inc}

interface

uses
  SysUtils,
  DIrr.Types, DIrr.Vector3D;

type
  //! Hold the different Mesh Types used for getMesh
  TQ3MeshIndex =
  (
    E_Q3_MESH_GEOMETRY,
    E_Q3_MESH_ITEMS,
    E_Q3_MESH_BILLBOARD,
    E_Q3_MESH_FOG,
    E_Q3_MESH_UNRESOLVED,
    E_Q3_MESH_SIZE
  );

	(*! used to customize Quake3 BSP Loader *)
  TQ3LevelLoadParameter = record
  public
    defaultLightMapMaterial: E_MATERIAL_TYPE;
    defaultModulate: E_MODULATE_FUNC;
    defaultFilter: E_MATERIAL_FLAG;
    patchTesselation: S32;
    verbose: S32;
    startTime: U32;
    endTime: U32;
    mergeShaderBuffer: S32;
    cleanUnResolvedMeshes: S32;
    loadAllShaders: S32;
    loadSkyShader: S32;
    alpharef: S32;
    swapLump: S32;
    swapHeader: S32;
    scriptDir: array[0..63] of C8;
  public
    procedure Init;
  end;

  //! A blend function for a q3 shader.
  TBlendFunc = record
  public
    typeMaterial: E_MATERIAL_TYPE;
    modulate: E_MODULATE_FUNC;
    Param0: F32;
    IsTransparent: U32;
  public
    procedure Init(AModulate: E_MODULATE_FUNC);
  end;

  TNoiser = record
  private class var
    RandomSeed: U32;
  public
    class function get: F32; static;
  public
    class constructor Create;
  end;

  //TQ3ModifierFunction =
  {$SCOPEDENUMS ON}
  TQ3MF =
  (
    UNKNOWN           =   -2,

    TCMOD             =   0,
    DEFORMVERTEXES    =   1,
    RGBGEN            =   2,
    TCGEN             =   3,
    MAP               =   4,
    ALPHAGEN          =   5,

    FUNCTION2         =   $10,
    SCROLL            =   FUNCTION2 + 1,
    SCALE             =   FUNCTION2 + 2,
    ROTATE            =   FUNCTION2 + 3,
    STRETCH           =   FUNCTION2 + 4,
    TURBULENCE        =   FUNCTION2 + 5,
    WAVE              =   FUNCTION2 + 6,

    IDENTITY          =   FUNCTION2 + 7,
    VERTEX            =   FUNCTION2 + 8,
    TEXTURE           =   FUNCTION2 + 9,
    LIGHTMAP          =   FUNCTION2 + 10,
    ENVIRONMENT       =   FUNCTION2 + 11,
    DOLLAR_LIGHTMAP   =   FUNCTION2 + 12,
    BULGE             =   FUNCTION2 + 13,
    AUTOSPRITE        =   FUNCTION2 + 14,
    AUTOSPRITE2       =   FUNCTION2 + 15,
    TRANSFORM         =   FUNCTION2 + 16,
    EXACTVERTEX       =   FUNCTION2 + 17,
    CONSTANT          =   FUNCTION2 + 18,
    LIGHTINGSPECULAR  =   FUNCTION2 + 19,
    MOVE              =   FUNCTION2 + 20,
    NORMAL            =   FUNCTION2 + 21,
    IDENTITYLIGHTING  =   FUNCTION2 + 22,

    WAVE_MODIFIER_FUNCTION  = $30,
    SINUS             =   WAVE_MODIFIER_FUNCTION + 1,
    COSINUS           =   WAVE_MODIFIER_FUNCTION + 2,
    SQUARE            =   WAVE_MODIFIER_FUNCTION + 3,
    TRIANGLE          =   WAVE_MODIFIER_FUNCTION + 4,
    SAWTOOTH          =   WAVE_MODIFIER_FUNCTION + 5,
    SAWTOOTH_INVERSE  =   WAVE_MODIFIER_FUNCTION + 6,
    NOISE             =   WAVE_MODIFIER_FUNCTION + 7
  );
  {$SCOPEDENUMS OFF}

  TModifierFunction = record
  public
    procedure Init;
    function Evaluate(dt: F32): F32;
    function GetMD3Normal(i, j: U32): TVector3DF;
    procedure GetModifierFunc(var Fill: TModifierFunction; const Str: AnsiString; var APos: U32);
  public
    // "tcmod","deformvertexes","rgbgen", "tcgen"
    masterfunc0: TQ3MF;
    // depends
    masterfunc1: TQ3MF;
    // depends
    func: TQ3MF;

    tcgen: TQ3MF;
    rgbgen: TQ3MF;
    alphagen: TQ3MF;

    Phase: F32;
    X: F32;
    Y: F32;
    Z: F32;
    count: U32;

    case Integer of
      1: (base: F32; amp: F32; frequency: F32; wave: F32);
      2: (bulgewidth: F32; bulgeheight: F32; bulgespeed: F32; div_: F32);
  end;

  // string helper.. TODO: move to generic files
  function IsEqual(const Str: AnsiString; var APos: U32; const list: array of PAnsiChar; listSize: Integer): S16;

  function GetAsFloat(const Str: AnsiString; var APos: U32): F32; inline;

  //! get a quake3 vector translated to irrlicht position (x,-z,y )
  function GetAsVector3DF(const Str: AnsiString; var APos: U32): TVector3DF; inline;



implementation

uses
  Math, AnsiStrings, DIrr.Math;

function IsEqual(const Str: AnsiString; var APos: U32; const list: array of PAnsiChar; listSize: Integer): S16;
var
  ACount, i, len: Integer;
begin
  ACount := Length(Str);
  if Integer(APos) < ACount then
  begin
    for i := 0 to listSize - 1 do
    begin
      len := AnsiStrings.StrLen(list[i]);
      if ACount < Integer(APos) + len then
        continue;
      if (Str[len] <> #0) and (Str[len] <> ' ') then
        continue;
      if AnsiStrings.StrLComp(PAnsiChar(@Str[APos]), list[i], Cardinal(len)) <> 0 then
        continue;

      APos := APos + U32(len) + 1;

      Result := i;
      Exit;
    end;

    Result := -1;
  end
  else
  begin
    Result := -2;
  end;
end;

function GetAsFloat(const Str: AnsiString; var APos: U32): F32; inline;
var
  i, Stat, EndPos: U32;
  s: AnsiString;
begin
  EndPos := APos;
  Stat := 0;
  for i := APos to Length(Str) do
  begin
    case Str[i] of
      '+', '-':
        if Stat = 0 then
          Stat := 1
        else
          break;
      '0'..'9':
        begin
          case Stat of
            0, 1: Stat := 2;
            3: Stat := 4;
            else
              break;
          end;
        end;
      '.':
        if Stat = 2 then
          Stat := 3
        else
          break;
      else
        break;
    end;

    EndPos := i;
  end;

  s := Copy(Str, APos, EndPos - APos + 1);
  APos := EndPos;
  Result := StrToFloat(String(s));
end;

function GetAsVector3DF(const Str: AnsiString; var APos: U32): TVector3DF; inline;
begin
  Result.X := GetAsFloat(Str, APos);
  Result.Y := GetAsFloat(Str, APos);
  Result.Z := GetAsFloat(Str, APos);
end;

{ TQ3LevelLoadParameter }

procedure TQ3LevelLoadParameter.Init;
begin
  defaultLightMapMaterial := EMT_LIGHTMAP_M4;
  defaultModulate := EMFN_MODULATE_4X;
  defaultFilter := EMF_BILINEAR_FILTER;
  patchTesselation := 8;
  verbose := 0;
  startTime := 0;
  endTime := 0;
  mergeShaderBuffer := 1;
  cleanUnResolvedMeshes := 1;
  loadAllShaders := 0;
  loadSkyShader := 0;
  alpharef := 1;
  swapLump := 0;
  {$IFDEF __BIG_ENDIAN__}
  swapHeader := 1;
  {$ELSE}
  swapHeader := 0;
  {$ENDIF}
  scriptDir := 'scripts'#0;
end;

{ TBlendFunc }

procedure TBlendFunc.Init(AModulate: E_MODULATE_FUNC);
begin
  modulate := AModulate;
  typeMaterial := EMT_SOLID;
  Param0 := 0;
  IsTransparent := 0;
end;

{ TNoiser }

class constructor TNoiser.Create;
begin
  RandomSeed := $69666966;
end;

class function TNoiser.get: F32;
var
  Value: F32;
begin
  RandSeed := RandSeed * 3631 + 1;
  Value := (RandSeed and $7FFF) * (1 / ($7FFF shr 1)) - 1;
  Result := Value;
end;

{ TModifierFunction }

function TModifierFunction.Evaluate(dt: F32): F32;
var
  x, y: f32;
begin
  // phase in 0 and 1..
  x := Fract((dt + Phase) * frequency);
  y := 0;

  case func of
    TQ3MF.SINUS: y := Sin(x * PI32 * 2);
    TQ3MF.COSINUS: y := Cos(x * PI32 * 2);
    TQ3MF.SQUARE:
      if x < 0.5 then
        y := 1
      else
        y := -1;
    TQ3MF.TRIANGLE:
      if x < 0.5 then
        y := 4 * x - 1
      else
        y := -4 * x + 3;
    TQ3MF.SAWTOOTH: y := x;
    TQ3MF.SAWTOOTH_INVERSE: y := 1 - x;
    TQ3MF.NOISE: y := TNoiser.get;
  end;

  Result := base + (y * amp);
end;

function TModifierFunction.GetMD3Normal(i, j: U32): TVector3DF;
var
  lng, lat: F32;
begin
  lng := i * 2 * PI32 / 255;
  lat := j * 2 * PI32 / 255;

  Result.SetValue(Cos(lat) * Sin(lng), Sin(lat) * Sin(lng), Cos(lng));
end;

procedure TModifierFunction.GetModifierFunc(var Fill: TModifierFunction;
  const Str: AnsiString; var APos: U32);
const
   FUNCLIST: array[0..6] of AnsiString =
   (
     'sin', 'cos',  'square', 'triangle', 'sawtooth', 'inversesawtooth', 'noise'
   );
begin
  if Str = '' then
    Exit;




end;

procedure TModifierFunction.Init;
begin
  masterfunc0 := TQ3MF.UNKNOWN;
  masterfunc1 := TQ3MF.UNKNOWN;
  func := TQ3MF.SINUS;
  tcgen := TQ3MF.TEXTURE;
  rgbgen := TQ3MF.IDENTITY;
  alphagen := TQ3MF.UNKNOWN;
  base := 0;
  amp := 1;
  Phase := 0;
  frequency := 1;
  wave := 1;
  X := 0;
  Y := 0;
  Z := 0;
  count := 0;
end;

end.